package com.ironman.compute.context;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.ironman.common.threadpool.ThreadPool;
import com.ironman.common.util.AccuracyUtil;
import com.ironman.compute.properties.SailingStandard;
import com.ironman.compute.properties.SailingStandardProperties;
import lombok.extern.slf4j.Slf4j;
import org.noear.snack.ONode;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;

@Slf4j
public class SailingShareContext {

    private final SailingStandardProperties sailingStandardProperties;

    /**
     * 船速cm/s
     */
    private Double speed;
    private final ReadWriteLock speedReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 航向°
     */
    private Double heading;
    private final ReadWriteLock headingReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 风环境数据是否在线
     */
    private final AtomicBoolean ENV_WIND_ONLINE = new AtomicBoolean();
    /**
     * 跟随教练艇编号
     */
    private String followTrainingBoatCode;
    private final ReadWriteLock followTrainingBoatCodeReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 磁盘角
     */
    private Float declination;
    private final ReadWriteLock declinationReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 平均风速
     */
    private Double windSpeed;
    private final ReadWriteLock windSpeedReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 平均风向
     */
    private Integer windDirection;
    private final ReadWriteLock windDirectionReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 自定义平均风速
     */
    private Double customWindSpeed;
    private final ReadWriteLock customWindSpeedReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 自定义平均风向
     */
    private Integer customWindDirection;
    private final ReadWriteLock customWindDirectionReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 风抵达时间
     */
    private Date ts;
    private final ReadWriteLock tsReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 船首向°
     */
    private Double yaw;
    private final ReadWriteLock yawReadWriteLock = new ReentrantReadWriteLock();

    /**
     * 平衡
     */
    private Double rol;
    private final ReadWriteLock rolReadWriteLock = new ReentrantReadWriteLock();

    public SailingShareContext(SailingStandardProperties sailingStandardProperties) {
        this.sailingStandardProperties = sailingStandardProperties;
        ThreadPool.SCHEDULED_EXECUTOR.scheduleAtFixedRate(() -> {
            Date ts = readTs();
            if (Objects.nonNull(ts)) {
                ENV_WIND_ONLINE.set(DateUtil.between(ts, new Date(), DateUnit.SECOND) <= 5);
            } else {
                ENV_WIND_ONLINE.set(false);
            }
            log.info("监测风数据是否在线 {}", ENV_WIND_ONLINE.get());
        }, 0, 5, TimeUnit.SECONDS);
    }

    private void writeDeclination(Float declination) {
        declinationReadWriteLock.writeLock().lock();
        try {
            this.declination = declination;
        } finally {
            declinationReadWriteLock.writeLock().unlock();
        }
    }

    private Float readDeclination() {
        declinationReadWriteLock.readLock().lock();
        try {
            return this.declination;
        } finally {
            declinationReadWriteLock.readLock().unlock();
        }
    }

    private void writeSpeed(Double speed) {
        speedReadWriteLock.writeLock().lock();
        try {
            this.speed = speed;
        } finally {
            speedReadWriteLock.writeLock().unlock();
        }
    }

    private Double readSpeed() {
        speedReadWriteLock.readLock().lock();
        try {
            return this.speed;
        } finally {
            speedReadWriteLock.readLock().unlock();
        }
    }

    private void writeHeading(Double heading) {
        headingReadWriteLock.writeLock().lock();
        try {
            this.heading = heading;
        } finally {
            headingReadWriteLock.writeLock().unlock();
        }
    }

    /**
     * true 磁北
     * false 真北
     */
    private Double readHeading(Boolean mn) {
        headingReadWriteLock.readLock().lock();
        try {
            if (Objects.isNull(this.heading)) {
                return null;
            }
            return mn ? this.heading - readDeclination() : this.heading;
        } finally {
            headingReadWriteLock.readLock().unlock();
        }
    }

    private void writeFollowTrainingBoatCode(String followTrainingBoatCode) {
        followTrainingBoatCodeReadWriteLock.writeLock().lock();
        try {
            this.followTrainingBoatCode = followTrainingBoatCode;
        } finally {
            followTrainingBoatCodeReadWriteLock.writeLock().unlock();
        }
    }

    private String readFollowTrainingBoatCode() {
        followTrainingBoatCodeReadWriteLock.readLock().lock();
        try {
            return this.followTrainingBoatCode;
        } finally {
            followTrainingBoatCodeReadWriteLock.readLock().unlock();
        }
    }

    private void writeWindSpeed(Double windSpeed) {
        windSpeedReadWriteLock.writeLock().lock();
        try {
            this.windSpeed = windSpeed;
        } finally {
            windSpeedReadWriteLock.writeLock().unlock();
        }
    }

    private Double readWindSpeed() {
        windSpeedReadWriteLock.readLock().lock();
        try {
            return this.windSpeed;
        } finally {
            windSpeedReadWriteLock.readLock().unlock();
        }
    }

    private void writeWindDirection(Integer windDirection) {
        windDirectionReadWriteLock.writeLock().lock();
        try {
            this.windDirection = windDirection;
        } finally {
            windDirectionReadWriteLock.writeLock().unlock();
        }
    }

    private Integer readWindDirection() {
        windDirectionReadWriteLock.readLock().lock();
        try {
            return this.windDirection;
        } finally {
            windDirectionReadWriteLock.readLock().unlock();
        }
    }

    private void writeCustomWindSpeed(Double customWindSpeed) {
        customWindSpeedReadWriteLock.writeLock().lock();
        try {
            this.customWindSpeed = customWindSpeed;
        } finally {
            customWindSpeedReadWriteLock.writeLock().unlock();
        }
    }

    private Double readCustomWindSpeed() {
        customWindSpeedReadWriteLock.readLock().lock();
        try {
            return this.customWindSpeed;
        } finally {
            customWindSpeedReadWriteLock.readLock().unlock();
        }
    }

    private void writeCustomWindDirection(Integer customWindDirection) {
        customWindDirectionReadWriteLock.writeLock().lock();
        try {
            this.customWindDirection = customWindDirection;
        } finally {
            customWindDirectionReadWriteLock.writeLock().unlock();
        }
    }

    private Integer readCustomWindDirection() {
        customWindDirectionReadWriteLock.readLock().lock();
        try {
            return this.customWindDirection;
        } finally {
            customWindDirectionReadWriteLock.readLock().unlock();
        }
    }

    private void writeTs(Date ts) {
        tsReadWriteLock.writeLock().lock();
        try {
            this.ts = ts;
        } finally {
            tsReadWriteLock.writeLock().unlock();
        }
    }

    private Date readTs() {
        tsReadWriteLock.readLock().lock();
        try {
            return this.ts;
        } finally {
            tsReadWriteLock.readLock().unlock();
        }
    }

    private void writeYaw(Double yaw) {
        yawReadWriteLock.writeLock().lock();
        try {
            this.yaw = yaw;
        } finally {
            yawReadWriteLock.writeLock().unlock();
        }
    }

    private Double readYaw() {
        yawReadWriteLock.readLock().lock();
        try {
            return this.yaw;
        } finally {
            yawReadWriteLock.readLock().unlock();
        }
    }

    private void writeRol(Double rol) {
        rolReadWriteLock.writeLock().lock();
        try {
            this.rol = rol;
        } finally {
            rolReadWriteLock.writeLock().unlock();
        }
    }

    private Double readRol() {
        rolReadWriteLock.readLock().lock();
        try {
            return this.rol;
        } finally {
            rolReadWriteLock.readLock().unlock();
        }
    }

    /**
     * 更新风环境数据
     */
    public void updateWind(String followTrainingBoatCode, Float declination, Double windSpeed, Integer windDirection, Double customWindSpeed, Integer customWindDirection, Date ts) {
        writeFollowTrainingBoatCode(followTrainingBoatCode);
        writeDeclination(declination);
        writeWindSpeed(windSpeed);
        writeWindDirection(windDirection);
        writeCustomWindSpeed(customWindSpeed);
        writeCustomWindDirection(customWindDirection);
        writeTs(ts);
    }

    public void updateGps(String mHmName, String color, List<String> recentGps, Consumer<ONode> gpsConsumer) {
        ONode root = ONode.newObject()
                .set("mHmName", mHmName)
                .set("color", color)
                .set("recentGpsSize", recentGps.size())
                .set("recentGps", recentGps);
        gpsConsumer.accept(root);
    }

    /**
     * 更新船速
     */
    public void updateSpeed(Double speed, Consumer<ONode> speedAndVmgAndWindConsumer) {
        writeSpeed(speed);
        speedAndVmgAndWindConsumer.accept(generateSpeedAndVmgAndWind());
    }

    /**
     * 更新航向
     */
    public void updateHeading(Double heading, Consumer<ONode> headingConsumer) {
        writeHeading(heading);
        headingConsumer.accept(generateHeading());
    }

    /**
     * 更新船首向
     */
    public void updateYaw(Double yaw, Consumer<ONode> yawConsumer) {
        writeYaw(yaw);
        yawConsumer.accept(generateYaw());
    }

    /**
     * 更新平衡
     */
    public void updateRol(Double rol, Consumer<ONode> rolConsumer) {
        writeRol(rol);
        rolConsumer.accept(generateRol());
    }

    /**
     * 更新主舵手心率
     */
    public void updateMHeartrate(Double mHeartrate, Consumer<ONode> heartrateConsumer) {
        ONode root = ONode.newObject();
        root.set("hrType", "0");
        root.set("hr", mHeartrate);
        heartrateConsumer.accept(root);
    }

    /**
     * 更新副舵手心率
     */
    public void updateSHeartrate(Double sHeartrate, Consumer<ONode> heartrateConsumer) {
        ONode root = ONode.newObject();
        root.set("hrType", "1");
        root.set("hr", sHeartrate);
        heartrateConsumer.accept(root);
    }

    /**
     * 船速
     */
    private ONode generateSpeedAndVmgAndWind() {
        ONode root = ONode.newObject();
        Double speed = readSpeed();
        root.set("speed", speed);
        if (ENV_WIND_ONLINE.get()) {
            SailingStandard sailingStandard = sailingStandardProperties.sailingStandard(readWindSpeed());
            root.set("standardSpeed", sailingStandard.getSpeed());
            SailingStandard customSailingStandard = sailingStandardProperties.sailingStandard(readCustomWindSpeed());
            root.set("customStandardSpeed", customSailingStandard.getSpeed());

            Double headingM = readHeading(true);

            Double angle = null;
            Double vmg = null;
            Integer windDirection = readWindDirection();
            if (Objects.nonNull(windDirection) && Objects.nonNull(headingM)) {
                double abs = Math.abs(headingM - windDirection);
                //角度c
                angle = abs <= 180 ? abs : 360 - abs;
                if (Objects.nonNull(speed)) {
                    // VMG=船速*COS(c)
                    vmg = Math.cos(Math.toRadians(angle)) * speed;
                }
            }
            root.set("angle", angle);
            root.set("vmg", vmg);
            root.set("standardVmg", sailingStandard.getVmg());

            Double customAngle = null;
            Double customVmg = null;
            Integer customWindDirection = readCustomWindDirection();
            if (Objects.nonNull(customWindDirection) && Objects.nonNull(headingM)) {
                double abs = Math.abs(headingM - customWindDirection);
                customAngle = abs <= 180 ? abs : 360 - abs;
                if (Objects.nonNull(speed)) {
                    customVmg = Math.cos(Math.toRadians(customAngle)) * speed;
                }
            }

            root.set("customAngle", customAngle);
            root.set("customVmg", customVmg);
            root.set("customStandardVmg", customSailingStandard.getVmg());

            root.set("windSourceBoatCode", readFollowTrainingBoatCode());
            root.set("windSpeed", readWindSpeed());
            root.set("windDirection", readWindDirection());
            root.set("customWindSpeed", readCustomWindSpeed());
            root.set("customWindDirection", readCustomWindDirection());
        }
        return root;
    }

    /**
     * 船首向
     */
    private ONode generateYaw() {
        ONode root = ONode.newObject();
        Double yaw = readYaw();
        root.set("yaw", yaw);
        String sailStatus = calSailStatus();
        root.set("leeway", calLeeway(sailStatus));
        root.set("sailStatus", sailStatus);
        return root;
    }

    /**
     * 航向
     */
    private ONode generateHeading() {
        ONode root = ONode.newObject();
        Double headingT = readHeading(false);
        Double headingM = readHeading(true);
        root.set("headingT", headingT);
        root.set("headingM", headingM);
        String sailStatus = calSailStatus();
        root.set("leeway", calLeeway(sailStatus));
        root.set("sailStatus", sailStatus);
        return root;
    }

    /**
     * 平衡
     */
    private ONode generateRol() {
        ONode root = ONode.newObject();
        root.set("rol", readRol());
        root.set("sailStatus", calSailStatus());
        return root;
    }

    /**
     * 航行状态 判断
     * 顺风左舷（D_P）
     * 顺风右舷（D_S）
     * 迎风左舷（U_P）
     * 迎风右舷（U_S）
     */
    private String calSailStatus() {
        Double headingM = readHeading(true);
        Integer windDirection = readWindDirection();
        if (Objects.nonNull(windDirection) && Objects.nonNull(headingM)) {
            double a = headingM - windDirection;
            // 顺风左舷
            if ((90 < a && a <= 180) || (-270 < a && a < -180)) {
                return "D_P";
            }
            // 顺风右舷
            if ((-180 < a && a < -90) || (180 < a && a < 270)) {
                return "D_S";
            }
            // 迎风左舷
            if ((0 <= a && a <= 90) || (-360 < a && a <= -270)) {
                return "U_P";
            }
            // 迎风右舷
            if ((270 <= a && a < 360) || (-90 <= a && a < 0)) {
                return "U_S";
            }
        }
        return null;
    }

    /**
     * 计算漂角
     */
    private String calLeeway(String sailStatus) {
        Double headingM = readHeading(true);
        Double yaw = readYaw();
        if (Objects.nonNull(headingM) && Objects.nonNull(yaw)) {
            double leeway;
            double e = headingM - yaw;
            if (Math.abs(e) <= 90) {
                leeway = e;
            } else if (e > 90) {
                leeway = e - 360;
            } else {
                leeway = 360 + e;
            }
            Integer w = AccuracyUtil.downDirectionInteger(leeway);
            if (w == 0) {
                return "0";
            }
            if ("U_P".equals(sailStatus)) {
                return w > 0 ? "-" + w : "+" + Math.abs(w);
            } else if ("U_S".equals(sailStatus)) {
                return w > 0 ? "+" + w : "-" + Math.abs(w);
            } else {
                return w > 0 ? "+" + w : "-" + Math.abs(w);
            }
        }
        return "";
    }
}
